package main

import (
	"context"
	"fmt"
	"log"
	"net"
	"strings"
	"time"

	pb "gitee.com/wangjq4214/schedule/proto/leader"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

type Register struct {
	ip   string
	port int

	status     *Status
	finishChan chan Task
	newChan    chan *[]Task

	leaderClient pb.LeaderServeClient
}

func NewRegister(addr string, ip string, port int, status *Status, finishChan chan Task, newChan chan *[]Task) *Register {
	conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Fatalf("did not connect: %v", err)
		return nil
	}

	return &Register{
		ip:           ip,
		port:         port,
		status:       status,
		finishChan:   finishChan,
		newChan:      newChan,
		leaderClient: pb.NewLeaderServeClient(conn),
	}
}

func (r *Register) buildRegisterBody() (*pb.RegisterReq, error) {
	if r.ip == "" {
		conn, err := net.Dial("udp", "www.baidu.com:80")
		if err != nil {
			log.Fatalf("did not connect: %v", err)
			return nil, err
		}
		defer conn.Close()
		r.ip = strings.Split(conn.LocalAddr().String(), ":")[0]
	}

	tmp := make([]*pb.FollerWaitingTask, 0)
	if len(r.newChan) != 0 {
		tasks := <-r.newChan

		for _, item := range *tasks {
			tmp = append(tmp, &pb.FollerWaitingTask{
				ID:      GenergateUUID(),
				CPU:     int32(item.CPU),
				Storage: int32(item.Storage),
				Time:    item.Time,
				Need:    item.Need,
				Position: &pb.DataPosition{
					ID: item.Position.ID,
					IP: item.Position.IP,
				},
			})
		}
	}

	return &pb.RegisterReq{
		IP:           r.ip + ":" + fmt.Sprint(r.port),
		CPUTotal:     r.status.CPUTotal,
		CPUMax:       r.status.CPUMax,
		CPUUsing:     r.status.CPUUsing,
		CPUWait:      r.status.CPUWait,
		StorageTotal: r.status.StorageTotal,
		StorageUsing: r.status.StorageUsing,
		Time:         r.status.Time,
		Task:         tmp,
	}, nil
}

func (r *Register) Beat() {
	timer := time.NewTimer(0)

	for {
		<-timer.C

		body, err := r.buildRegisterBody()
		if err != nil {
			timer.Reset(3 * time.Second)
			continue
		}

		// Contact the server and print out its response.
		func() {
			ctx, cancel := context.WithTimeout(context.Background(), time.Second)
			defer cancel()
			res, err := r.leaderClient.Register(ctx, body)
			if err != nil {
				log.Printf("Could not send beat: %v", err)
			} else {
				log.Printf("Msg: %s", res.GetStatus().GetMsg())
			}
		}()

		timer.Reset(3 * time.Second)
	}
}

func (r *Register) Report() {
	for {
		task := <-r.finishChan
		// Contact the server and print out its response.
		ctx, cancel := context.WithTimeout(context.Background(), time.Second)

		res, err := r.leaderClient.ReportTaskStatus(ctx, &pb.ReportTaskStatusReq{
			ID:          task.UUID,
			Status:      *pb.ReportTaskStatusReq_FINISH.Enum(),
			FinishTime:  task.FinishTime,
			ReceiveTime: task.ReceiveTime,
			StartTime:   task.StartTime,
			ReadyTime:   task.ReadyTime,
		})
		if err != nil {
			log.Printf("Could not send report: %v", err)
			continue
		}
		log.Printf("Msg: %s", res.GetStatus().GetMsg())

		cancel()
	}
}
